﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Text.RegularExpressions;
using System.Security.Cryptography;
using System.Xml.Linq;
using System.Diagnostics;

namespace Mengsk.ERP.Orders.TDOrderInterfaces.Paipai
{
    class PaipaiOrderInterface : TDOrderInterfaceBase
    {
        private const string PAIPAI_POP = "http://api.paipai.com";
        private const string PAIPAI_POP_MODIFYSELLERCOMMENT = "/deal/modifySellerNote.xhtml";
        private const string PAIPAI_POP_DETAIL = "/deal/sellerSearchDealList.xhtml";
        private const string PAIPAI_POP_MODIFYADDRESS = "/user/modifyReceiverAddress.xhtml";
        private const string PAIPAI_POP_GETBUYERADDRESS = "/user/getReceiverAddressList.xhtml";
        private const string PAIPAI_POP_CLOSEORDER = "/deal/sellerCancelDeal.xhtml";
        private const string PAIPAI_POP_BUYERSEARCH = "/deal/buyerSearchDealList.xhtml";
        private const string PAIPAI_POP_DELIVERY = "/wuliu/getWuliuDeal.xhtml";
        private const string PAIPAI_POP_DELIVERYTRACK = "/wuliu/getTrackInfo.xhtml";
        private const int PAIPAI_POP_PAGESIZE = 20;
        private const String FORMAT_XML = "xml";
        private const String CHARSET = "utf-8";
        private const string SIGN = "sign";
        // 请求方法
        private String method = "post";
        Random random = new Random((int)DateTime.Now.Ticks);

        private string shop = null;

        public string UIN { get; private set; }

        public string AccessToken { get; private set; }

        public string AppOAuthID { get; private set; }

        public string AppOAuthKey { get; private set; }

        public override Shop Shop
        {
            get { return (Orders.Shop)(Enum.Parse(typeof(Orders.Shop), this.shop)); }
        }

        public override bool AcceptOrder(string popId)
        {
            return popId.StartsWith(this.UIN);
        }

        public override void Initialize(System.Xml.Linq.XElement xelement, string shopName)
        {
            this.UIN = xelement.Elements("UIN").First().Value;
            this.AccessToken = xelement.Elements("AccessToken").First().Value;
            this.AppOAuthID = xelement.Elements("AppOAuthID").First().Value;
            this.AppOAuthKey = xelement.Elements("AppOAuthKey").First().Value;
            this.shop = shopName;
            if (string.IsNullOrWhiteSpace(this.UIN))
            {
                throw new Exception("UIN is empty");
            }

            if (string.IsNullOrWhiteSpace(this.AccessToken))
            {
                throw new Exception("AccessToken is empty");
            }

            if (string.IsNullOrWhiteSpace(this.AppOAuthID))
            {
                throw new Exception("AppOAuthID is empty");
            }
        }

        private string GetTime()
        {
            Int64 retval = 0;
            DateTime st = new DateTime(1970, 1, 1);
            TimeSpan t = (DateTime.Now.ToUniversalTime() - st);
            retval = (Int64)(t.TotalMilliseconds + 0.5);
            return retval.ToString();
        }

        private int GetRandomValue()
        {
            int ret = (random.Next() + 10000) % 100000000;
            return ret;
        }

        private string ComputeSignCode(string method, string apiURI, Dictionary<string, string> parameters, string appOAuthKey, string encoding)
        {
            //获取编码
            Encoding encodingMethod = Encoding.GetEncoding(encoding);

            //编码头部
            string encodedApiURI = Http.HttpUtil.UrlEncode(apiURI, encodingMethod);
            string encodedRequestHeader = method.ToUpper() + "&" + encodedApiURI;

            //编码参数
            //去除空的
            foreach (var para in parameters.Where(para => string.IsNullOrWhiteSpace(para.Key) || string.IsNullOrWhiteSpace(para.Value)).ToArray())
            {
                parameters.Remove(para.Key);
            }
            //升序
            var sortedParams = new SortedDictionary<string, string>(parameters, StringComparer.Ordinal);
            //拼接请求参数
            string requestPara = string.Join("&", sortedParams.Select(item => item.Key + "=" + item.Value));
            //对请求参数进行编码
            string encodedrequestPara = Http.HttpUtil.UrlEncode(requestPara, Encoding.GetEncoding(encoding));

            string fullRequest = encodedRequestHeader + "&" + encodedrequestPara;

            //计算签名
            Byte[] signatureKey = Encoding.ASCII.GetBytes(this.AppOAuthKey + "&");
            HMACSHA1 hmacsha1 = new HMACSHA1(signatureKey);
            hmacsha1.ComputeHash(Encoding.GetEncoding(encoding).GetBytes(fullRequest));
            byte[] hash = hmacsha1.Hash;
            string signature = Convert.ToBase64String(hash);
            return signature;
        }

        private string InvokeOpenApi(string serviceUrl, Dictionary<string, string> parameters)
        {
            parameters["timeStamp"] = GetTime();
            parameters["randomValue"] = GetRandomValue().ToString();
            parameters["uin"] = this.UIN;
            parameters["accessToken"] = this.AccessToken;
            parameters["appOAuthID"] = this.AppOAuthID;
            parameters["format"] = FORMAT_XML;
            parameters["charset"] = CHARSET;
            if (parameters.ContainsKey("sign"))
            {
                parameters.Remove("sign");
            }
            string sin = ComputeSignCode(method, serviceUrl, parameters, this.AppOAuthKey, CHARSET);
            parameters["sign"] = sin;
            string ret = Http.HttpUtil.DoPost(PAIPAI_POP + serviceUrl, parameters);
            if (string.IsNullOrWhiteSpace(ret))
            {
                throw new Exception("请求API返回空");
            }
            return ret;
        }

        private void InvokeOpenApiAndCheck(string serviceUrl, Dictionary<string, string> parameters)
        {
            string ret = this.InvokeOpenApi(serviceUrl, parameters);

            XDocument xDoc = XDocument.Parse(ret);
            if (xDoc.Root.Element("errorCode").Value.Trim() != "0")
            {
                throw new Exception(xDoc.Root.Element("errorMessage").Value);
            }
        }

        private XDocument InvokeOpenApiAndReturnXDoc(string serviceUrl, Dictionary<string, string> parameters)
        {
            string ret = this.InvokeOpenApi(serviceUrl, parameters);
            XDocument xDoc = XDocument.Parse(ret);
            if (xDoc.Root.Element("errorCode").Value.Trim() != "0")
            {
                throw new Exception(xDoc.Root.Element("errorMessage").Value);
            }
            return xDoc;
        }

        private bool DownloadPage(int page, ref int totalPage, List<Order> orders, Dictionary<string, string> para, bool raiseEvent, bool downloadItemImage)
        {
            int totalCount = 0;

            para["pageIndex"] = page.ToString();
            para["pageSize"] = PAIPAI_POP_PAGESIZE.ToString();
            XDocument xDoc = this.InvokeOpenApiAndReturnXDoc(PAIPAI_POP_DETAIL, para);
            if (xDoc.Root.Element("pageIndex").Value.Trim() != page.ToString())
            {
                throw new Exception("要下载的页，与返回的页数据不匹配");
            }

            totalPage = int.Parse(xDoc.Root.Element("pageTotal").Value.Trim());
            totalCount = int.Parse(xDoc.Root.Element("countTotal").Value.Trim());
            int count = 0;
            foreach (XElement xe in xDoc.Root.Elements("dealList").First().Elements("dealInfo"))
            {
                OrderType type = xe.Element("dealPayType").Value.Trim() == "OFF_LINE" ? OrderType.TRADE0FF : OrderType.ONLINE;

                Order order = new Order
                {
                    ERPID = Guid.NewGuid(),
                    Shop = this.Shop,
                    POPID = xe.Element("dealCode").Value,
                    Type = type,
                    Source = OrderSource.None,
                    State = xe.Element("dealState").Value.Trim(),
                    Flag = (OrderFlag)(Enum.Parse(typeof(OrderFlag), xe.Element("dealNoteType").Value)),
                    Money = (float.Parse(xe.Element("dealPayFeeTotal").Value) / 100),
                    PayType = OrderPayType.CAIFUTONG,
                    CreateTime = DateTime.Parse(xe.Element("createTime").Value),
                    PayTime = type == OrderType.ONLINE ? DateTime.Parse(xe.Element("payTime").Value) : DateTime.Parse(xe.Element("recvfeeReturnTime").Value),
                    ReciveFeeTime = type == OrderType.ONLINE ? DateTime.Parse(xe.Element("payReturnTime").Value) : DateTime.Parse(xe.Element("recvfeeTime").Value),

                    DeliveryTime = DateTime.Parse(xe.Element("sellerConsignmentTime").Value),
                    ReciveTime = DateTime.MinValue,
                    PPCOD = xe.Element("ppCodId").Value,
                    DeliveryCompany = "",
                    DeliveryNumber = "",

                    Name = xe.Element("buyerUin").Value,
                    NickName = xe.Element("buyerName").Value,
                    RealName = xe.Element("receiverName").Value,
                    PhoneMobile = xe.Element("receiverMobile").Value,
                    Phone = xe.Element("receiverPhone").Value,
                    Address = xe.Element("receiverAddress").Value,
                    BuyerComment = xe.Element("buyerRemark").Value,
                    SellerComment = xe.Element("dealNote").Value,
                    DetailLink = xe.Element("dealDetailLink").Value,
                    LastUpdateUser = "System",
                    LastUpdateTimeErp = DateTime.Now,
                    LastUpdateTimePop = DateTime.Parse(xe.Element("lastUpdateTime").Value),
                    Comment = "",
                };
                foreach (XElement item in xe.Element("itemListNew").Elements("itemInfo"))
                {
                    OrderItem orderItem = new OrderItem();
                    orderItem.ERPID = order.ERPID;
                    orderItem.POPSubID = item.Element("dealSubCode").Value;
                    orderItem.Name = item.Element("itemName").Value;
                    orderItem.Code = item.Element("itemCode").Value;
                    orderItem.SupplyNumber = item.Element("itemLocalCode").Value;
                    orderItem.InternalName = item.Element("stockLocalCode").Value;
                    orderItem.SKUID = item.Element("skuId").Value;
                    string[] pro = item.Element("stockAttr").Value.Split('|');
                    if (pro.Length >= 2)
                    {
                        orderItem.Color = pro[0].Split(':')[1];
                        orderItem.Size = pro[1].Split(':')[1];
                    }
                    else
                    {
                        orderItem.Color = "";
                        orderItem.Size = "";
                    }
                    if (pro.Length >= 3)
                    {
                        orderItem.Edtion = pro[2].Split(':')[1];
                    }
                    else
                    {
                        orderItem.Edtion = "";
                    }
                    orderItem.Count = int.Parse(item.Element("itemDealCount").Value);
                    orderItem.Money = float.Parse(item.Element("itemRetailPrice").Value) / 100;
                    orderItem.SellMoney = (float.Parse(item.Element("itemDealPrice").Value) - float.Parse(item.Element("itemAdjustPrice").Value)) / 100;
                    orderItem.DetailWebLink = item.Element("itemDetailLink").Value;
                    orderItem.Image = downloadItemImage ? Http.HttpUtil.DownloadRemoteImageFile(item.Element("itemPic80").Value) : new byte[0];
                    orderItem.POPID = order.POPID;
                    order.Items.Add(orderItem);
                }
                count++;
                orders.Add(order);
                if (raiseEvent)
                    this.OnOrderDownloading(new OrderDownloadEventArgs(this.Shop, totalCount, (page - 1) * PAIPAI_POP_PAGESIZE + count, order));
            }
            return totalPage > page;
        }

        private Order[] DownloadOrders(Dictionary<string, string> para, bool raiseEvent, bool downloadItemImage)
        {
            int totalPage = 0;
            int page = 1;
            List<Order> orders = new List<Order>();
            bool hasMore = true;

            while (hasMore)
            {
                for (int i = 0; i < 10; i++)
                {
                    try
                    {
                        hasMore = this.DownloadPage(page++, ref totalPage, orders, para, raiseEvent, downloadItemImage);
                        break;
                    }
                    catch (OutOfMemoryException ex)
                    {
                        GC.Collect();
                        Debug.WriteLine(ex.Message);
                        hasMore = false;
                    }
                    catch (Exception ex)
                    {
                        Debug.WriteLine(DateTime.Now + " : " + ex.Message);
                        Debug.WriteLine("检查到超时，等待1S后重试");
                        Thread.Sleep(1000);
                        page--;
                        hasMore = false;
                    }
                    if (i >= 10)
                    {
                        break;
                    }
                    continue;
                }
            }
            return orders.ToArray();
        }

        public override Order[] DownloadOrders(OrderDownloadArgs args)
        {
            List<Order> orders = new List<Order>();
            Dictionary<string, string> para = new Dictionary<string, string>();
            para["sellerUin"] = this.UIN;
            para["listItem"] = args.DownLoadItems ? "1" : "0";
            para["timeType"] = "CREATE";
            if (string.IsNullOrWhiteSpace(args.OrderState) == false)
            {
                para["dealState"] = args.OrderState;
            }

            if (string.IsNullOrWhiteSpace(args.Id) == false)
            {
                para["dealCode"] = args.Id;
            }

            Order[] items = null;
            //计算是否需要查找历史订单
            if (DateTime.Now.Subtract(args.StartTime).TotalDays >= 90)
            {
                para["historyDeal"] = "1";
                para["timeBegin"] = args.StartTime.ToString("yyyy-MM-dd HH:mm:00");
                para["timeEnd"] = args.EndTime.AddDays(-89).ToString("yyyy-MM-dd HH:mm:00");
                items = this.DownloadOrders(para, args.RaiseEvents, args.DwonloadItemsImage);
                orders.AddRange(items);
                args.StartTime = DateTime.Now.AddDays(-90);
            }
            para.Remove("historyDeal");
            para["timeBegin"] = args.StartTime.ToString("yyyy-MM-dd HH:mm:00");
            para["timeEnd"] = args.EndTime.ToString("yyyy-MM-dd HH:mm:00");
            items = this.DownloadOrders(para, args.RaiseEvents, args.DwonloadItemsImage);

            if (orders.Count < 1)
            {
                return items;
            }

            foreach (var item in items)
            {
                if (orders.Contains(item))
                {
                    Debug.WriteLine(string.Format("Order:{0},{1} is alreay exist", item.POPID, item.CreateTime));
                    continue;
                }
                orders.Add(item);
            }
            return orders.ToArray();
        }

        public override Order DownLoadOrder(string orderID, DateTime orderCreateTime, bool downloadItemImage)
        {
            Dictionary<string, string> para = new Dictionary<string, string>();
            para["sellerUin"] = this.UIN;
            para["listItem"] = "1";
            para["dealCode"] = orderID;
            Order[] orders = null;

            if (DateTime.Now.Subtract(orderCreateTime).TotalDays >= 80)
            {
                para["historyDeal"] = "1";
                orders = this.DownloadOrders(para, false, downloadItemImage);
            }

            if (orders != null && orders.Length > 0)
            {
                return orders[0];
            }

            para.Remove("historyDeal");
            orders = this.DownloadOrders(para, false, downloadItemImage);
            if (orders.Length > 0)
            {
                return orders[0];
            }

            return null;
        }

        public override void UpdateOrderComment(string orderID, OrderFlag flag, string sellerComment)
        {
            if (flag == OrderFlag.UN_LABEL)
            {
                throw new Exception("flag value :" + flag + " 不能为 : " + flag);
            }
            Dictionary<string, string> para = new Dictionary<string, string>();
            para["sellerUin"] = this.UIN;
            para["dealCode"] = orderID;
            para["dealNote"] = sellerComment;
            para["dealNoteType"] = flag.ToString();
            this.InvokeOpenApiAndCheck(PAIPAI_POP_MODIFYSELLERCOMMENT, para);

        }

        public override void UpdateOrderAddress(string orderID, string name, string address)
        {
            throw new NotImplementedException();
        }

        public override void CloseOrder(string orderID)
        {
            Dictionary<string, string> para = new Dictionary<string, string>();
            para["sellerUin"] = this.UIN;
            para["dealCode"] = orderID;
            para["closeReason"] = "16";
            this.InvokeOpenApiAndCheck(PAIPAI_POP_CLOSEORDER, para);
        }

        public override void GetDeliveryInfo(string dealId, ref string deliveryCompany, ref string deliveryNumber)
        {
            Dictionary<string, string> para = new Dictionary<string, string>();
            para["dealId"] = dealId;
            var item = this.InvokeOpenApiAndReturnXDoc(PAIPAI_POP_DELIVERY, para);
            int count = int.Parse(item.Root.Element("totalNum").Value);
            if (count < 1)
            {
                deliveryCompany = "";
                deliveryNumber = "";
                return;
            }
            try
            {
                deliveryCompany = item.Root.Element("dealList").Element("deal").Element("wlCompanyName").Value;
                deliveryNumber = item.Root.Element("dealList").Element("deal").Element("wlMailNo").Value;
            }
            catch
            {
                deliveryCompany = "";
                deliveryNumber = "";
                return;
            }
        }

        public override string GetDeliveryTrack(string dealId, string deliveryCompany, string deliveryNumber)
        {
            Dictionary<string, string> para = new Dictionary<string, string>();
            para["company"] = deliveryCompany;
            para["wuliuCarryId"] = deliveryNumber;

            XDocument xDoc = this.InvokeOpenApiAndReturnXDoc(PAIPAI_POP_DELIVERYTRACK, para);
            List<string> tarck = new List<string>();

            foreach (XElement xe in xDoc.Root.Element("ItemLists").Elements())
            {
                string line = xe.Element("Time").Value + " : " + xe.Element("Description").Value;
                if (tarck.Count < 1 || (tarck.Count >= 1 && tarck[tarck.Count - 1] != line))
                {
                    tarck.Add(line);
                }
            }
            return string.Join(Environment.NewLine, tarck.ToArray());
        }
    }
}
